package com.soxing.webrain.brush.service;

import com.jmatio.io.MatFileReader;
import com.jmatio.types.*;
import com.soxing.webrain.brush.api.IFeedBackService;
import com.soxing.webrain.brush.domain.view.R;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;


@Service
@Slf4j
public class FeedBackServiceImpl implements IFeedBackService {

//    @Value("${feedback.file}")
//    String filePath;
    //用于正方形 或者指针型的反馈

    /**
     * 用于在指定文件夹中加载最新的bci文件，出错返回null
     * @param filePath
     * @return
     */
    @Value("${feedback.key}")
    String oneValueKey;

    public String getOneValue(String filePath) {
        log.info("开始解析单值模式:"+filePath);
        File target = getLastModifiedFile(new File(filePath),".ini");

        //如果不存在文件的话
        if(target==null){
            return null;
        }
        Path p = Paths.get(target.getPath());

        List<String> lines = null;
        try {
            lines = Files.readAllLines(p);
        } catch (IOException e) {
            //e.printStackTrace();
            return null;
        }
        String result = null;
        for(String h:lines){
            if(h.contains(oneValueKey)){
                if(h.split("=").length<2){
                    return null;
                }
                result = h.split("=")[1];
            }
        }
        return result;
    }

    /**
     * 用于返回2d的功率谱文件
     * @param filePath
     * @return
     */
    public R get2dPowerValue(String filePath){

        log.info("开始解析2d功率谱模式:"+filePath);
        //找到最新的mat文件 以后可能是其他的标识
        File target = getLastModifiedFile(new File(filePath),".mat");
        if(target==null){
            return null;
        }
        R result = null;
        try {
            result =  get2dPower(target.getPath());
        }catch (Exception e){
            e.printStackTrace();
        }
        return result==null?R.error().data("mode",2):result;

    }

    //D:\brain_project\file\source

    /**
     * 这里是返回溯源结果的文件，溯源解析的mat文件还不是非常确定，以后再更改
     * @param filePath
     * @return
     */
    public R get3dSourceImage(String filePath){
        log.info("开始解析3d溯源文件模式:"+filePath);
        File target = getLastModifiedFile(new File(filePath),".mat");
        if(target==null){
            return null;
        }
        //R result = null;
        float[] data = null;
        try{
            data = read3dMat(target.getPath());
        }catch (Exception e){
            return R.error().message(e.getMessage()).data("mode",4);
        }
        if(data==null){
            return R.error().data("mode",4);
        }
        return R.ok().data("data",data).data("mode",4);
    }

    @Override
    public R get3dNetValue(String filePath) {

        log.info("开始解析3d脑网络数据:"+filePath);
        File target = getLastModifiedFile(new File(filePath),".mat");
        if(target==null){
            return null;
        }
        MatFileReader reader = null;
        try {
            reader = new MatFileReader(target);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        MLStructure r1 = (MLStructure) reader.getContent().get("feedback_results");
        double oneValue = ((MLDouble)r1.getField("feedback_value")).getArray()[0][0];
        MLStructure EEG_results = (MLStructure) r1.getField("network");
        //MLStructure EEG_results = (MLStructure) reader.getContent().get("network");
        MLStructure parameter = (MLStructure)EEG_results.getField("parameter");

        MLStructure zonelocs = (MLStructure) r1.getField("zonelocs");

        int[] zd = zonelocs.getDimensions();
        ArrayList<ArrayList<String>> zoneList = new ArrayList<>();
        //MLArray labels = zonelocs.getField("labels");
        String[] zoneLabels = new String[zd[1]];

        for(int i=0;i<zd[1];i++){
            zoneLabels[i] = ((MLChar)(((MLCell)zonelocs.getField("labels",i)).get(0))).getString(0);

            ArrayList<String> list = new ArrayList<>();

            int nowlen = ((MLCell)zonelocs.getField("zone_chan",i)).getDimensions()[1];

            for(int j=0;j<nowlen;j++){

                list.add(((MLChar)(((MLCell)zonelocs.getField("zone_chan",i)).get(j))).getString(0));
            }
            zoneList.add(list);

        }

        //用的脑网络的示例mat文件中没有bandname
        Chanlocs chanlocs = Chanlocs.getChanlocsInfo(parameter);
        MLDouble data = (MLDouble)r1.getField("feedback_network");

        //第二维度和第三维度混合了，处理一下，转成三维数组  这里还是要留意一个情况的出现，就是眼电参与了脑网络的计算？会出现这种情况吗？
        final double[][] temp = data.getArray();
        int len = temp.length;

        //java 中为了顺着思维来 第一维度是频段 第二纬度 和第三维度才是导联  matlab中存储是 导联 导联 频段
        double[][][] MZScoreMean = new double[temp[0].length/len][len][len];
        for(int i=0;i<temp.length;i++){
            for(int j=0;j<temp[0].length;j++){
                //将二维数据装回三维数据 并换一下顺序
                MZScoreMean[j/temp.length][i][j%temp.length] = temp[i][j];

            }
        }

        //return R.ok().data("power_mean",Power_relative_mean).data("theta",chanlocs.getTheta_data()).data("radius",chanlocs.getTheta_data()).data("bandName",chanlocs.getBandName()).data("labels",chanlocs.getLabels_data());
        R result = R.ok().data("oneValue",oneValue).data("M_zscore_mean",MZScoreMean).data("chanlocs",chanlocs).data("mode",3).data("zoneLabel",zoneLabels).data("zone_chan",zoneList);

        return result==null?R.error().data("mode",3):result;
    }

    public float[] read3dMat(String filePath){

        float[] result = null;
        File f = new File(filePath);
        MatFileReader reader = null;
        try {
            reader = new MatFileReader(f);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //System.out.println();

        //溯源结构体还不是特别的完整 还不能确定的 先按测试的写
        MLArray mlArray=reader.getMLArray("now1");
        MLSingle d=(MLSingle)mlArray;
        final int[] dimensions = d.getDimensions();
        float[] record = new float[dimensions[0]];
        result = record;
        for(int i=0;i<dimensions[0];i++){
            record[i] = d.get(i);
        }

        return result;
        //System.out.println();

    }




    /**
     * * 因为挺多地方要用导联信息的，专门弄个类来进行封装吧。
     * * 对于没有坐标信息的导联，一律进行了滤除操作。
     */
    @Data
    static class Chanlocs{


        double[] theta_data;
        double[] radius_data;
        String[] labels_data;
        double[] x;
        double[] y;
        double[] z;
        String[] bandName;
        double[][] bandLimitArray;
        int chanlocsNum;
        //用于记录没有电极坐标的导联数量，没有电极坐标信息的导联数将不会写回返回结果中
        int bad;
        //跳过的导联记为1
        int[] record;


        public static Chanlocs getChanlocsInfo(MLStructure parameter){


            String[] bandname_data = new String[1];
            //因为bandName可能不存在哦
//            try {
//                MLCell bandname = (MLCell)parameter.getField("bandName");
//                int[] bandname_numbers = bandname.getDimensions();
//                int bandname_number = bandname_numbers[0];
//                bandname_data = new String[bandname_number];
//                for(int i =0;i<bandname_number;i++)
//                {
//                    MLChar bandname1 = (MLChar) bandname.get(i);
//                    bandname_data[i] = bandname1.getString(0);
//
//                }
//
//            }catch (Exception e){
//
//                e.printStackTrace();
//            }

            //bandLimit 这个应该一般是存在的吧
            MLDouble bandLimit = (MLDouble)parameter.getField("bandLimit");
            final double[][] bandLimitArray = bandLimit.getArray();


            //((MLSingle)chanlocs.getField("X", i)).get(0)


            MLStructure chanlocs = (MLStructure)parameter.getField("chanlocs");
            int[] dimensions = chanlocs.getDimensions();
            int dimension = Math.max(dimensions[1],dimensions[0]);
            double[] test = new double[dimension];
            int record_bad = 0;
            for(int i = 0;i<dimension;i++)
            {
                try{
//                    MLDouble theta = (MLDouble) chanlocs.getField("theta", i);
//                    double[][] theta_f = theta.getArray();
                    test[i] = getValue(chanlocs.getField("theta", i));

                }catch (Exception e)
                {
                    e.printStackTrace();
                    record_bad++;
                }

            }
            double[] theta_data = new double[dimension-record_bad];
            double[] radius_data = new double[dimension-record_bad];
            String[] labels_data = new String[dimension-record_bad];
            double[] x = new double[dimension-record_bad];
            double[] y = new double[dimension-record_bad];
            double[] z = new double[dimension-record_bad];

            int bad = 0; //记录不是脑电的导联的数量
            int[] record = new int[dimension];//记录一下非头皮的导联 如果不是就标号为一
            for(int i=0;i<dimension;i++)
            {
                try {//有的导联是眼电或者是心电这里的 所以要写一下逻辑
                    //MLDouble theta = (MLDouble) chanlocs.getField("theta", i);
                    //double[][] theta_f = theta.getArray();
                    theta_data[i-bad] = getValue(chanlocs.getField("theta", i));


//                    MLDouble radius = (MLDouble) chanlocs.getField("radius", i);
//                    double[][] radius_f = radius.getArray();
                    radius_data[i-bad] = getValue(chanlocs.getField("radius", i));

                    MLChar label = (MLChar) chanlocs.getField("labels", i);
                    labels_data[i-bad] = label.getString(0);

//                    MLDouble xValue = (MLDouble) chanlocs.getField("X", i);
//                    double[][] x_f = xValue.getArray();
                    x[i-bad] = getValue(chanlocs.getField("X", i));;

//                    MLDouble yValue = (MLDouble) chanlocs.getField("Y", i);
//                    double[][] y_f = yValue.getArray();
                    y[i-bad] = getValue(chanlocs.getField("Y", i));;

//                    MLDouble zValue = (MLDouble) chanlocs.getField("Z", i);
//                    double[][] z_f = zValue.getArray();
                    z[i-bad] = getValue(chanlocs.getField("Z", i));;


                }catch(Exception e)
                {

                    record[i] = 1;
                    bad++;
                }


            }
            Chanlocs result = new Chanlocs();
            result.setBandLimitArray(bandLimitArray);
            result.setBandName(bandname_data);
            result.setLabels_data(labels_data);
            result.setX(x);
            result.setY(y);
            result.setZ(z);
            result.setRadius_data(radius_data);
            result.setTheta_data(theta_data);
            result.setBad(bad);
            result.setChanlocsNum(dimension);
            result.setRecord(record);


            return result;
        }


        //由于计算结果会出现各种类型，所以加入这些判断
        public static double getValue(MLArray r) throws Exception {
            if(r instanceof MLDouble){
                return ((MLDouble)r).get(0);
            }
            if(r instanceof MLSingle){
                return ((MLSingle) r).get(0);
            }
            if(r instanceof MLInt16){
                return ((MLInt16) r).get(0);
            }
            if(r instanceof MLInt32){
                return ((MLInt32) r).get(0);
            }
            if(r instanceof MLUInt64){
                return ((MLUInt64) r).get(0);
            }
            if(r instanceof MLInt8){
                return ((MLInt8) r).get(0);
            }
            if (r instanceof  MLUInt8){
                if(((MLUInt8) r).get(0)<0){
                    return 256 + ((MLUInt8) r).get(0);
                }
                return ((MLUInt8) r).get(0);
            }
//            if( r instanceof  MLUInt32){
//            }
            log.warn("没有匹配的类型:"+r.getClass().toString());
            throw new Exception();

        }



    }

    public R get2dPower(String EEGfile)
    {
        //String EEGfile = "D:\\matlab\\webrain_download\\4002-power2\\power_sub001.mat";
        File f = new File(EEGfile);
        MatFileReader reader = null;
        try {
            reader = new MatFileReader(f);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

// 文件中只有一个 1*1 的 struct, name = "EEG_results"
        MLStructure r1 = (MLStructure) reader.getContent().get("feedback_results");
        MLStructure EEG_results = (MLStructure) r1.getField("power");
        String type = EEG_results.getField("type").contentToString();
        String filename = EEG_results.getField("filename").contentToString();

        MLStructure parameter = (MLStructure)EEG_results.getField("parameter");
        Chanlocs chanlocs = Chanlocs.getChanlocsInfo(parameter);
        MLDouble tmp = (MLDouble)r1.getField("feedback_power");
        //单值
        double oneValue = ((MLDouble)r1.getField("feedback_value")).getArray()[0][0];
        double[][] Power_relative_mean = (tmp.getArray());

//        int time = Power_relative_mean[0].length;
//        double[][] power_data = new double[chanlocs.getChanlocsNum()-chanlocs.getBad()][time];
//        int record_number=0;
//        for(int i =0;i<chanlocs.getChanlocsNum();i++)
//        {
//            if (chanlocs.getRecord()[i] ==1)
//            {
//                record_number++;
//                continue;
//            }
//            for(int j=0; j<time;j++)
//            {
//                power_data[i-record_number][j] = Power_relative_mean[i][j];
//            }
//        }
        return R.ok().data("oneValue",oneValue).data("power_mean",Power_relative_mean).data("theta",chanlocs.getTheta_data()).data("radius",chanlocs.getRadius_data()).data("bandName",chanlocs.getBandName()).data("labels",chanlocs.getLabels_data()).data("mode",2);
    }



    public File getLastModifiedFile(File directory,String keyword) {
        File[] files = directory.listFiles();
        if(files==null){
            return null;
        }
        if (files.length == 0) return null;
        Arrays.sort(files, new Comparator<File>() {
            public int compare(File o1, File o2) {
                return new Long(o2.lastModified()).compareTo(o1.lastModified());
            }});

        //找到bac格式的数据
        for(File item:files){
            if(item.getName().contains(keyword)){
                return item;
            }
        }
        return null;
    }
}
